package org.drools.devguide.chapter03;

import org.drools.core.util.StringUtils;

import java.io.*;
import java.util.UUID;

public class FileManager {
    //private Set<File> files;
    private File root;

    public FileManager setUp() {
        this.root = getRootDirectory();
        return this;
    }

    public void tearDown() {
        deleteDir(root);
    }

    public File newFile(String name) {
        File file = new File(getRootDirectory(),
                name);
        //files.add( file );
        return file;
    }

    private File newFile(String path,
                         String fileName) {
        File file = new File(getRootDirectory(),
                path);

        file.mkdir();
        //files.add( file );
        return new File(file,
                fileName);
    }

    public File newFile(File dir,
                        String name) {
        File file = new File(dir,
                name);
        //files.add( file );
        return file;
    }

    public File getRootDirectory() {
        if (this.root != null) {
            return this.root;
        }
        File tmp = new File(System.getProperty("java.io.tmpdir"));
        File f = new File(tmp,
                "__drools__" + UUID.randomUUID().toString());
        //files.add( f );
        if (f.exists()) {
            if (f.isFile()) {
                throw new IllegalStateException("The temp directory exists as a file. Nuke it now !");
            }
            deleteDir(f);
            f.mkdir();
        } else {
            f.mkdir();
        }
        this.root = f;
        return this.root;
    }

    public void deleteDir(File dir) {
        // Will throw RuntimeException is anything fails to delete
        String[] children = dir.list();
        for (String child : children) {
            File file = new File(dir,
                    child);
            if (file.isFile()) {
                deleteFile(file);
            } else {
                deleteDir(file);
            }
        }

        deleteFile(dir);
    }

    public void deleteFile(File file) {
        // This will attempt to delete a file 5 times, calling GC and Sleep between each iteration
        // Sometimes windows takes a while to release a lock on a file.
        // Throws an exception if it fails to delete
        if (!file.delete()) {
            int count = 0;
            while (!file.delete() && count++ < 5) {
                System.gc();
                try {
                    Thread.sleep(250);
                } catch (InterruptedException e) {
                    throw new RuntimeException("This should never happen");
                }
            }
        }

        if (file.exists()) {
            try {
                if (System.getProperty("os.name").toLowerCase().contains("win")) {
                    // on windows platforms with JDK 6 or lower, a known bug will prevent
                    // the files from being released until the JVM terminates
                    // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4950148
                } else {
                    throw new RuntimeException("Unable to delete file:" + file.getCanonicalPath());
                }
            } catch (IOException e) {
                throw new RuntimeException("Unable to delete file", e);
            }
        }

    }

    public void write(File f,
                      String text) throws IOException {
        if (f.exists()) {
            // we want to make sure there is a time difference for lastModified and lastRead checks as Linux and http often round to seconds
            // http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=1&t=019789
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException("Unable to sleep");
            }
        } else {
            // ensure that the directory structures exists
            new File(f.getParent()).mkdirs();
        }

        // Attempt to write the file
        BufferedWriter output = new BufferedWriter(new FileWriter(f));
        output.write(text);
        output.close();

        // Now check the file was written and re-attempt if it was not        
        // Need to do this for testing, to ensure the texts are read the same way, otherwise sometimes you get tail \n sometimes you don't
        String t1 = StringUtils.toString(new StringReader(text));

        int count = 0;
        while (!t1.equals(StringUtils.toString(new BufferedReader(new FileReader(f)))) && count < 5) {
            // The file failed to write, try 5 times, calling GC and sleep between each iteration
            // Sometimes windows takes a while to release a lock on a file            
            System.gc();
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                throw new RuntimeException("This should never happen");
            }
            output = new BufferedWriter(new FileWriter(f));
            output.write(text);
            output.close();
            count++;
        }

        if (count == 5) {
            throw new IOException("Unable to write to file:" + f.getCanonicalPath());
        }
    }

    public File write(String fileName,
                      String text) throws IOException {
        File f = newFile(fileName);

        write(f,
                text);

        return f;

    }

    public File write(String path,
                      String fileName,
                      String text) throws IOException {
        File f = newFile(path,
                fileName);

        write(f,
                text);

        return f;
    }

    public String readInputStreamReaderAsString(InputStreamReader in) throws IOException {
        StringBuffer fileData = new StringBuffer(1000);
        BufferedReader reader = new BufferedReader(in);
        char[] buf = new char[1024];
        int numRead = 0;
        while ((numRead = reader.read(buf)) != -1) {
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();
    }
}